import * as fs from 'fs';
import * as path from 'path';
import { ApiEndpoint, ApiParameter, ApiExample } from '../extension';

export class TypeScriptParser {
  async parseFile(filePath: string): Promise<ApiEndpoint[]> {
    const content = fs.readFileSync(filePath, 'utf8');
    const lines = content.split('\n');
    const endpoints: ApiEndpoint[] = [];

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim();
      
      // Look for Express.js, NestJS, and other TypeScript route definitions
      if (line.includes('app.get(') || 
          line.includes('app.post(') || 
          line.includes('app.put(') || 
          line.includes('app.delete(') ||
          line.includes('app.patch(') ||
          line.includes('router.get(') ||
          line.includes('router.post(') ||
          line.includes('router.put(') ||
          line.includes('router.delete(') ||
          line.includes('router.patch(') ||
          line.includes('@Get(') ||
          line.includes('@Post(') ||
          line.includes('@Put(') ||
          line.includes('@Delete(') ||
          line.includes('@Patch(')) {
        
        const endpoint = await this.parseEndpoint(lines, i, filePath);
        if (endpoint) {
          endpoints.push(endpoint);
        }
      }
    }

    return endpoints;
  }

  private async parseEndpoint(lines: string[], startLine: number, filePath: string): Promise<ApiEndpoint | null> {
    const line = lines[startLine];
    let method = 'GET';
    let path = '';
    let description = '';
    let parameters: ApiParameter[] = [];
    let returnType = 'any';
    let returnDescription = '';

    // Extract HTTP method and path from various decorators
    if (line.includes('app.get(') || line.includes('router.get(') || line.includes('@Get(')) {
      method = 'GET';
      path = this.extractPath(line);
    } else if (line.includes('app.post(') || line.includes('router.post(') || line.includes('@Post(')) {
      method = 'POST';
      path = this.extractPath(line);
    } else if (line.includes('app.put(') || line.includes('router.put(') || line.includes('@Put(')) {
      method = 'PUT';
      path = this.extractPath(line);
    } else if (line.includes('app.delete(') || line.includes('router.delete(') || line.includes('@Delete(')) {
      method = 'DELETE';
      path = this.extractPath(line);
    } else if (line.includes('app.patch(') || line.includes('router.patch(') || line.includes('@Patch(')) {
      method = 'PATCH';
      path = this.extractPath(line);
    }

    // Look for method definition in next few lines
    let methodName = '';
    for (let i = startLine + 1; i < Math.min(startLine + 10, lines.length); i++) {
      const methodLine = lines[i];
      if (methodLine.includes('async') || methodLine.includes('function') || methodLine.includes('(')) {
        methodName = this.extractMethodName(methodLine);
        returnType = this.extractReturnType(methodLine);
        parameters = this.extractTypeScriptParameters(methodLine);
        break;
      }
    }

    // Look for JSDoc comments above the decorator
    for (let i = startLine - 1; i >= Math.max(0, startLine - 10); i--) {
      const commentLine = lines[i];
      if (commentLine.includes('/**')) {
        description = this.extractJSDocDescription(lines, i);
        break;
      }
    }

    if (!methodName) {
      methodName = `${method.toLowerCase()}Handler`;
    }

    const examples = this.generateExamples(method, path, parameters, returnType);

    return {
      name: methodName,
      path,
      method,
      description: description || `${method} endpoint for ${methodName}`,
      parameters,
      returnType,
      returnDescription,
      examples,
      filePath,
      lineNumber: startLine + 1
    };
  }

  private extractPath(line: string): string {
    // Handle different decorator patterns
    const pathMatch = line.match(/["']([^"']+)["']/);
    if (pathMatch) {
      return pathMatch[1];
    }
    
    // Handle NestJS decorators
    const nestPathMatch = line.match(/@(Get|Post|Put|Delete|Patch)\(["']([^"']+)["']\)/);
    if (nestPathMatch) {
      return nestPathMatch[2];
    }
    
    return '/';
  }

  private extractMethodName(line: string): string {
    // Try various patterns for method names
    const asyncMatch = line.match(/async\s+(\w+)\s*\(/);
    if (asyncMatch) {
      return asyncMatch[1];
    }

    const functionMatch = line.match(/function\s+(\w+)/);
    if (functionMatch) {
      return functionMatch[1];
    }

    const arrowMatch = line.match(/const\s+(\w+)\s*=/);
    if (arrowMatch) {
      return arrowMatch[1];
    }

    const methodMatch = line.match(/(\w+)\s*\(/);
    if (methodMatch) {
      return methodMatch[1];
    }

    return 'unknown';
  }

  private extractReturnType(line: string): string {
    // Look for return type annotations
    const returnMatch = line.match(/\)\s*:\s*([^{]+)/);
    if (returnMatch) {
      return returnMatch[1].trim();
    }

    const asyncReturnMatch = line.match(/async\s+\w+\s*\([^)]*\)\s*:\s*([^{]+)/);
    if (asyncReturnMatch) {
      return asyncReturnMatch[1].trim();
    }

    return 'any';
  }

  private extractTypeScriptParameters(line: string): ApiParameter[] {
    const params: ApiParameter[] = [];
    
    // Common Express.js/NestJS parameters
    params.push({
      name: 'req',
      type: 'Request',
      description: 'Express request object',
      required: true
    });

    params.push({
      name: 'res',
      type: 'Response',
      description: 'Express response object',
      required: true
    });

    // Look for additional parameters in method signature
    const paramMatch = line.match(/\(([^)]+)\)/);
    if (paramMatch) {
      const paramString = paramMatch[1];
      const paramList = paramString.split(',').map(p => p.trim());
      
      for (const param of paramList) {
        if (param && param !== 'req' && param !== 'res' && param !== '') {
          const parts = param.split(':');
          const name = parts[0].trim();
          const type = parts.length > 1 ? parts[1].trim() : 'any';
          
          params.push({
            name,
            type,
            description: `Parameter ${name} of type ${type}`,
            required: true
          });
        }
      }
    }
    
    return params;
  }

  private extractJSDocDescription(lines: string[], startLine: number): string {
    let description = '';
    
    for (let i = startLine; i < lines.length; i++) {
      const line = lines[i];
      if (line.includes('*/')) break;
      
      if (line.includes('*')) {
        const content = line.replace(/^\s*\*\s*/, '').trim();
        if (content && !content.startsWith('@')) {
          description += content + ' ';
        }
      }
    }
    
    return description.trim();
  }

  private generateExamples(method: string, path: string, parameters: ApiParameter[], returnType: string): ApiExample[] {
    const examples: ApiExample[] = [];

    // cURL example
    let curlExample = `curl -X ${method} "http://localhost:3000${path}"`;
    if (method !== 'GET' && parameters.length > 2) { // More than req, res
      curlExample += ` \\\n  -H "Content-Type: application/json" \\\n  -d '{\n`;
      const paramData = parameters
        .filter(p => p.name !== 'req' && p.name !== 'res')
        .map(p => `    "${p.name}": "value"`)
        .join(',\n');
      curlExample += paramData + '\n  }';
    }

    examples.push({
      title: 'cURL Request',
      description: 'Example cURL request for this endpoint',
      code: curlExample,
      language: 'bash'
    });

    // TypeScript fetch example
    let tsExample = `// Using fetch API with TypeScript\n`;
    if (method === 'GET') {
      tsExample += `const response = await fetch("http://localhost:3000${path}");\nconst data: ${returnType} = await response.json();\nconsole.log(data);`;
    } else {
      tsExample += `const response = await fetch("http://localhost:3000${path}", {\n  method: "${method}",\n  headers: {\n    "Content-Type": "application/json",\n  },\n`;
      if (parameters.length > 2) {
        tsExample += `  body: JSON.stringify({\n`;
        const paramData = parameters
          .filter(p => p.name !== 'req' && p.name !== 'res')
          .map(p => `    ${p.name}: "value"`)
          .join(',\n');
        tsExample += paramData + '\n  })\n';
      }
      tsExample += `});\n\nconst data: ${returnType} = await response.json();\nconsole.log(data);`;
    }

    examples.push({
      title: 'TypeScript Fetch',
      description: 'Example using TypeScript fetch API',
      code: tsExample,
      language: 'typescript'
    });

    return examples;
  }
} 